﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Models;
using Providers;
using XMLProviders.Extensions;

namespace XMLProviders
{
    /// <summary>
    /// Manage saved database lists
    /// </summary>
    public class SavedDatabasesProvider : ISavedDatabasesProvider
    {
        /// <summary>
        /// Save a list of databases for later use
        /// </summary>
        /// <param name="listName">The name of the database list to save</param>
        /// <param name="databases">The list of databases to save</param>
        /// <param name="errorMsg">Any error message generated when trying to save the list</param>
        /// <returns>True if the list was saved</returns>
        public bool SaveList(string listName, List<Database> databases, out string errorMsg)
        {
            bool result;

            errorMsg = string.Empty;

            XDocument xDoc = new XDocument(
                new XElement("DatabaseConfiguration",
                    new XElement("dblFileVersion", "2"),
                    new XElement("ArrayOfDatabase",
                                 from db in databases
                                 select (new XElement("Database",
                                                      new XElement("ServerName", db.DatabaseServer.ServerName),
                                                      new XElement("ServerID", db.DatabaseServer.ServerID.ToString()),
                                                      ConvertConnectionToXML(db.DatabaseServer.ServerConnection),
                                                      new XElement("DBName", db.DatabaseName))))));

            FileStream xmlFile = null;

            try
            {
                // Create a file stream to write the xml document to disk, if the file already exists it is overwritten
                xmlFile = new FileStream(listName, FileMode.Create);
                xDoc.Save(xmlFile);

                // If we get here then the file was saved so return true
                result = true;
            }
            catch (Exception ex)
            {
                // Something has gone wrong so return false
                errorMsg = ex.Message;
                result = false;
            }
            finally
            {
                // Whatever happens make sure the stream is closed if it was created
                if (xmlFile != null)
                {
                    xmlFile.Close();
                    xmlFile.Dispose();
                }
            }

            return result;
        }

        /// <summary>
        /// Load a list of databases
        /// </summary>
        /// <param name="listName">The name of the database list to load</param>
        /// <param name="errorMsg">Any error message generated when trying to load the list</param>
        /// <returns>A list of databases</returns>
        public List<Database> LoadList(string listName, out string errorMsg)
        {
            List<Database> result = null;

            errorMsg = string.Empty;

            if (File.Exists(listName))
            {
                try
                {
                    XDocument xDoc = XDocument.Load(listName);

                    var fileVersion = xDoc.Descendants("dblFileVersion").FirstOrDefault();

                    if ((fileVersion != null) && (fileVersion.Value == "2"))
                    {
                        // Extract each server from the XML file and create a list of databases
                        result =
                            (from db in xDoc.Descendants("Database")
                             select new Database(
                                 new Server(db.GetChildElementStringValue("ServerName"),
                                            db.GetChildElementGuidValue("ServerID"),
                                            db.GetChildElementDictionary("ServerConnection")),
                                 db.GetChildElementStringValue("DBName"))).ToList();
                    }
                    else
                    {
                        // Legacy (SuperQuery 1.x and 2.0.x) database list files
                        result =
                            (from db in xDoc.Descendants("Database")
                             select new Database(
                                 new Server(db.GetChildElementStringValue("ServerName"),
                                     Guid.Empty,
                                     new Dictionary<string, string>() { 
                                        {"UserName", db.GetChildElementStringValue("DBUser")},
                                        {"Password",db.GetChildElementStringValue("DBPassword")},
                                        {"WindowsAuthentication",db.GetChildElementBoolValue("WindowsAuthentication",false).ToString()}
                                    }),
                                 db.GetChildElementStringValue("DBName"))).ToList();
                    }
                }
                catch (Exception ex)
                {
                    errorMsg = ex.Message;
                }
            }

            return result;
        }

        /// <summary>
        /// Convert a dictionary containing connection settings into XML elements
        /// </summary>
        /// <param name="connectionSettings">A dictionary containing connection settings</param>
        /// <returns>The connection settings in the form of a tree of XElements</returns>
        private XElement ConvertConnectionToXML(Dictionary<string, string> connectionSettings)
        {
            XElement result = new XElement("ServerConnection");

            // Loop through the connection settings dictionary and add its contents as child XML Elements
            if (connectionSettings != null)
            {
                foreach (KeyValuePair<string, string> item in connectionSettings)
                {
                    result.Add(new XElement(item.Key, item.Value));
                }
            }

            return result;
        }
    }
}
